#include "windows.h"

#include "Log.h"
#include "MessageTypes.h"
#include "Randomizer.h"
#include "BufMngr.h"

using namespace std;

BufferManager::BufferManager() :
		error(false), data(NULL), len(2048uL), pos(0uL), end_pos(0uL), read_pos(0uL), is_encrypted(false), key_index(0L) {
	InitializeCriticalSectionAndSpinCount(&crit_section, 4000uL);
	data = new char[len];
	if (data) {
		memset(data, 0, len);
	}
}

BufferManager::BufferManager(DWORD bufferlen) :
		error(false), data(NULL), len(2048uL), pos(0uL), end_pos(0uL), read_pos(0uL), is_encrypted(false), key_index(0L) {
	InitializeCriticalSectionAndSpinCount(&crit_section, 4000uL);
	len = bufferlen;
	data = new char[len];
	memset(data, 0, len);
	if (data) {
		memset(data, 0, len);
	}
}

BufferManager::~BufferManager() {
	SAFEDELA(data);
}

DWORD BufferManager::GetLen(void) {
	return (len);
}

DWORD BufferManager::GetEndPos(void) {
	return (end_pos);
}

DWORD BufferManager::GetReadPos(void) {
	return (read_pos);
}

char* BufferManager::GetData(void) {
	return (data);
}

char* BufferManager::GetCurData(void) {
	return (data + read_pos);
}

void BufferManager::Dump(void) {
	FILE* handle = fopen("dump.dmp", "wb");
	fwrite(data + read_pos, 1, end_pos - read_pos, handle);
	fclose(handle);
}

void BufferManager::Cut(DWORD size) {
	if ((!error) && (size)) {
		DWORD address, index;

		if ((this->read_pos + size) <= this->end_pos) {
			address = this->read_pos + (DWORD) this->data;
			index = 0uL;
			if ((this->read_pos + size) < this->end_pos) {
				do {
					*(char*) address = *(char*) (address + size);
					++index;
					++address;
				}
				while ((this->read_pos + index + size) < this->end_pos);
			}
			this->end_pos -= size;
		}
		else {
			this->error = 1;
			LOG_MSG("[BufferManager::Cut] Tried to cut more data than available in the buffer!\n");
		}
	}
}

bool BufferManager::Reset(void) {
	end_pos = 0uL;
	read_pos = 0uL;

	if (len > 2048uL) {
		len = 2048uL;
		SAFEDELA(data);
		data = new char[len];
	}

	if (data) {
		return (true);
	}
	else {
		return (false);
	}

}

void BufferManager::GrowBuf(DWORD size) {
	if (size + end_pos >= len) {
		do {
			len <<= 1uL;
		}
		while (size + end_pos >= len);

		{
			char* buffer = new char[len];

			memcpy(buffer, data, end_pos);
			SAFEDELA(data);
			data = buffer;
		}
	}
}

bool BufferManager::IsEmpty(void) {
	return (read_pos >= end_pos);
}
bool BufferManager::IsError(void) {
	return error;
}

void BufferManager::Lock(void) {
	EnterCriticalSection(&crit_section);
}

void BufferManager::Unlock(void) {
	LeaveCriticalSection(&crit_section);
}

void BufferManager::LockReset(void) {
	Lock();
	Reset();
	Unlock();
}

bool BufferManager::IsValidMsg(DWORD msgid) {
	switch (msgid) {
	case NET_ONCHECKUID4:
	case 0xFFFFFFFFUL:
	case NET_ONAUTOMAPSINFO:
	case 0xBD5AA7AAUL:
	case 0xBD5AA8AAUL:
	case NET_ONUSERHOLOSTR:
	case 0xBD5AA4AAUL:
	case 0xBD5AA5AAUL:
	case NET_ONHOLOINFO:
	case NET_ONQUEST_0:
	case NET_ONQUEST_1:
	case NET_ONPROTOITEMDATA:
	case NET_ONCHECKUID3:
	case NET_ONMSGDATA:
	case NET_ONGLOBALENTRANCES:
	case 0xBD5A88AAUL:
	case 0xBD5A89AAUL:
	case NET_ONGLOBALINFO:
	case NET_ONVIEWMAP:
	case 0xBD5A86AAUL:
	case 0xBD5A82AAUL:
	case 0xBD5A7FAAUL:
	case 0xBD5A80AAUL:
	case NET_ONDROPTIMERS:
	case NET_ONRUNCLIENTSCRIPT:
	case 0xBD5A7CAAUL:
	case NET_ONSHOWSCREEN:
	case NET_ONLOADMAP:
	case NET_ONMAP:
	case 0xBD5A76AAUL:
	case 0xBD5A74AAUL:
	case NET_ONGAMEINFO:
	case NET_ONPLAYERSBARTERSETHIDE:
	case NET_ONCHOSENTALK:
	case 0xBD5A70AAUL:
	case NET_ONPLAYERSBARTER:
	case 0xBD5A6EAAUL:
	case 0xBD5A6DAAUL:
	case NET_ONPLAYSOUND_1:
	case NET_ONFLYEFFECT:
	case NET_ONPLAYSOUND_0:
	case NET_ONEFFECT:
	case NET_ONCRITTERANIMATE:
	case NET_ONCRITTERSETANIMS:
	case NET_ONCRITTERITEMDATA:
	case NET_ONCRITTERACTION:
	case NET_ONCRITTERKNOCKOUT:
	case NET_ONCRITTERMOVEITEM:
	case NET_ONSOMEITEM:
	case 0xBD5A56AAUL:
	case 0xBD5A57AAUL:
	case 0xBD5A55AAUL:
	case NET_ONCONTAINERINFO:
	case 0xBD5A54AAUL:
	case 0xBD5A52AAUL:
	case NET_ONCHECKUID2:
	case 0xBD5A51AAUL:
	case NET_ONANIMATEITEM:
	case NET_ONCHANGEITEMONMAP:
	case 0xBD5A49AAUL:
	case NET_ONERASEITEMFROMMAP:
	case NET_ONADDITEMONMAP:
	case NET_ONCHOSENERASEITEM:
	case 0xBD5A44AAUL:
	case NET_ONCHOSENADDITEM:
	case NET_ONCRAFTRESULT:
	case NET_ONCHOSENCLEARITEMS:
	case 0xBD5A3EAAUL:
	case 0xBD5A3CAAUL:
	case NET_ONCRAFTASK:
	case NET_ONCRITTERPARAM:
	case NET_ONCRITTERXY:
	case NET_ONCHOSENPARAMS:
	case NET_ONCHOSENPARAM:
	case NET_ONCRITTERMOVE:
	case 0xBD5A2BAAUL:
	case 0xBD5A2CAAUL:
	case NET_ONCRITTERDIR:
	case 0xBD5A25AAUL:
	case 0xBD5A29AAUL:
	case NET_ONMAPTEXTMSG:
	case NET_ONTEXTMSG_1:
	case NET_ONMAPTEXT:
	case NET_ONTEXTMSG_0:
	case NET_ONCHECKUID1:
	case 0xBD5A1FAAUL:
	case NET_ONTEXT:
	case NET_ONITEMLEXEMS:
	case 0xBD5A15AAUL:
	case NET_ONCRITTERLEXEMS:
	case NET_ONREMOVECRITTER:
	case 0xBD5A0BAAUL:
	case 0xBD5A0CAAUL:
	case 0xBD5A0AAAUL:
	case NET_ONPING:
	case NET_ONCHECKUID0:
	case NET_ONREGSUCCESS:
	case NET_SENDLOGIN:
	case NET_ONLOGINSUCCESS:
	case 0xBD5A03AAUL:
		return (true);
		break;
	default:
		return (false);
		break;
	}

	return (false);
}

void BufferManager::Pop(char *buffer, DWORD size) {
	if ((!error) && (size)) {
		if ((size + read_pos) <= end_pos) {
			int key;

			key = EncryptKey(size);
			CopyBuf(data + read_pos, buffer, NULL, key, size);
			read_pos += size;
		}
		else {
			error = true;
			LOG_MSG("[BufferManager::Pop] Tried to pop more data than available in the buffer!\n");
		}
	}
}

/** @warning Function shall not be used! */
void BufferManager::Push(char *buffer, char* unknown, DWORD size) {
	if ((!error) && (size) && (unknown)) {
		int key;

		if ((size + end_pos) >= len) {
			GrowBuf(size);
		}
		key = EncryptKey(size);
		CopyBuf(buffer, data + end_pos, unknown, key, size);
		end_pos += size;
	}
}

void BufferManager::Push(char *buffer, DWORD size, bool mode) {
	if ((!error) && (size)) {
		int key;

		if ((size + end_pos) >= len) {
			GrowBuf(size);
		}

		if (mode) {
			key = 0L;
		}
		else {
			key = EncryptKey(size);
		}
		CopyBuf(buffer, data + end_pos, NULL, key, size);
		end_pos += size;
	}
}

void BufferManager::CopyBuf(char const *buffer_src, char* buffer_dest, char const* unknown, DWORD key, DWORD size) {
	if (unknown) {
		/// @warning Functionality is not implemented!
	}
	else {
		if (size) {
			// test if not multiple of 4
			if (size & 3) {
				for (DWORD i = 0UL; i < size; i++) {
					buffer_dest[i] = key ^ buffer_src[i];
				}
			}
			else {
				size = ((size - 1) >> 2) + 1;
				for (DWORD i = 0UL; i < size; i++) {
					((DWORD*) buffer_dest)[i] = key ^ ((DWORD*) buffer_src)[i];
				}
			}
		}
	}
}

DWORD BufferManager::EncryptKey(int offset) {
	DWORD key;

	if (is_encrypted) {
		key = keys_table[key_index];

		key_index += offset;
		if ((key_index < 0L) || (key_index >= 50L)) {
			key_index %= 50L;
			if (key_index < 0) {
				key_index += 50L;
			}
		}
	}
	else {
		key = 0uL;
	}
	return (key);
}

int BufferManager::MoveReadPos(int offset) {
	int result;

	read_pos += offset;
	result = offset;
	if (is_encrypted) {
		key_index += offset;
		result = key_index;
		if ((key_index < 0L) || (key_index >= 50L)) {
			key_index %= 50L;
			result /= 50L;
			if (key_index < 0) {
				key_index += 50L;
			}
		}
	}
	return result;
}

/** Check if a complete message is available in the receive buffer.
 *
 * @return true if a complete message is available which have to be processed. Return false otherwise.
 */
bool BufferManager::NeedProcess(void) {
	if ((read_pos + 4uL) > end_pos) {
		return (false);
	}

	for (;;) {
		DWORD key;
		DWORD msgid;

		if (is_encrypted) {
			key = keys_table[key_index];
			if (key_index < 0L || key_index >= 50L) {
				key_index %= 50L;
				if (key_index < 0) {
					key_index += 50L;
				}
			}
		}
		else {
			key = 0uL;
		}
		// decrypt message id
		msgid = key ^ *(DWORD*) (data + read_pos);
		// test all supported message IDs
		switch ((DWORD) msgid) {
		case NET_ONSOMEITEM:
			return ((end_pos) >= (read_pos + 131UL));
			break;
		case 0xBD5A55AAUL:
			return ((end_pos) >= (read_pos + 23UL));
			break;
		case 0xBD5A56AAUL:
			return ((end_pos) >= (read_pos + 13UL));
			break;
		case 0xBD5A57AAUL:
			return ((end_pos) >= (read_pos + 9UL));
			break;
		case 0xBD5A54AAUL:
			return ((end_pos) >= (read_pos + 18UL));
			break;
		case NET_ONANIMATEITEM:
			return ((end_pos) >= (read_pos + 10UL));
			break;
		case 0xBD5A51AAUL:
			return ((end_pos) >= (read_pos + 15UL));
			break;
		case 0xBD5A52AAUL:
			return ((end_pos) >= (read_pos + 10UL));
			break;
		case NET_ONERASEITEMFROMMAP:
			return ((end_pos) >= (read_pos + 9UL));
			break;
		case NET_ONADDITEMONMAP:
			return ((end_pos) >= (read_pos + 135UL));
			break;
		case NET_ONCHANGEITEMONMAP:
			return ((end_pos) >= (read_pos + 128UL));
			break;
		case 0xBD5A49AAUL:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case 0xBD5A44AAUL:
			return ((end_pos) >= (read_pos + 10UL));
			break;
		case NET_ONCHOSENERASEITEM:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case NET_ONCHOSENADDITEM:
			return ((end_pos) >= (read_pos + 131UL));
			break;
		case NET_ONCRAFTRESULT:
			return ((end_pos) >= (read_pos + 5UL));
			break;
		case NET_ONCHOSENCLEARITEMS:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case 0xBD5A3EAAUL:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case NET_ONCHOSENPARAMS:
			return ((end_pos) >= (read_pos + 4004UL));
			break;
		case NET_ONCHOSENPARAM:
			return ((end_pos) >= (read_pos + 10UL));
			break;
		case NET_ONCRITTERPARAM:
			return ((end_pos) >= (read_pos + 14UL));
			break;
		case NET_ONCRITTERXY:
			return ((end_pos) >= (read_pos + 13UL));
			break;
		case 0xBD5A2BAAUL:
			return ((end_pos) >= (read_pos + 12UL));
			break;
		case 0xBD5A2CAAUL:
			return ((end_pos) >= (read_pos + 12UL));
			break;
		case NET_ONCRITTERMOVE:
			return ((end_pos) >= (read_pos + 16UL));
			break;
		case NET_ONCRITTERDIR:
			return ((end_pos) >= (read_pos + 9UL));
			break;
		case NET_ONTEXTMSG_0:
			return ((end_pos) >= (read_pos + 15UL));
			break;
		case NET_ONMAPTEXTMSG:
			return ((end_pos) >= (read_pos + 18UL));
			break;
		case 0xBD5A29AAUL:
			return ((end_pos) >= (read_pos + 5UL));
			break;
		case NET_ONREMOVECRITTER:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case NET_ONPING:
			return ((end_pos) >= (read_pos + 5UL));
			break;
		case NET_ONREGSUCCESS:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_SENDLOGIN:
			return ((end_pos) >= (read_pos + 201UL));
			break;
		case NET_ONLOGINSUCCESS:
			return ((end_pos) >= (read_pos + 12UL));
			break;
		case 0xBD5A76AAUL:
			return ((end_pos) >= (read_pos + 9UL));
			break;
		case NET_ONPLAYERSBARTERSETHIDE:
			return ((end_pos) >= (read_pos + 134UL));
			break;
		case 0xBD5A74AAUL:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_ONGAMEINFO:
			return ((end_pos) >= (read_pos + 55UL));
			break;
		case NET_ONPLAYERSBARTER:
			return ((end_pos) >= (read_pos + 13UL));
			break;
		case 0xBD5A67AAUL:
			return ((end_pos) >= (read_pos + 9UL));
			break;
		case 0xBD5A6DAAUL:
			return ((end_pos) >= (read_pos + 10UL));
			break;
		case 0xBD5A6EAAUL:
			return ((end_pos) >= (read_pos + 34UL));
			break;
		case NET_ONPLAYSOUND_1:
			return ((end_pos) >= (read_pos + 12UL));
			break;
		case NET_ONEFFECT:
			return ((end_pos) >= (read_pos + 12UL));
			break;
		case NET_ONFLYEFFECT:
			return ((end_pos) >= (read_pos + 22UL));
			break;
		case NET_ONPLAYSOUND_0:
			return ((end_pos) >= (read_pos + 108UL));
			break;
		case NET_ONCRITTERSETANIMS:
			return ((end_pos) >= (read_pos + 20UL));
			break;
		case NET_ONCRITTERANIMATE:
			return ((end_pos) >= (read_pos + 19UL));
			break;
		case NET_ONCRITTERITEMDATA:
			return ((end_pos) >= (read_pos + 129UL));
			break;
		case NET_ONCRITTERACTION:
			return ((end_pos) >= (read_pos + 17UL));
			break;
		case NET_ONCRITTERKNOCKOUT:
			return ((end_pos) >= (read_pos + 20UL));
			break;
		case 0xBD5A86AAUL:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_ONDROPTIMERS:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case 0xBD5A82AAUL:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_ONVIEWMAP:
			return ((end_pos) >= (read_pos + 16UL));
			break;
		case 0xBD5A80AAUL:
			return ((end_pos) >= (read_pos + 33UL));
			break;
		case NET_ONSHOWSCREEN:
			return ((end_pos) >= (read_pos + 13UL));
			break;
		case 0xBD5A7CAAUL:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_ONLOADMAP:
			return ((end_pos) >= (read_pos + 23UL));
			break;
		case 0xBD5A7BAAUL:
			return ((end_pos) >= (read_pos + 23UL));
			break;
		case 0xBD5AA5AAUL:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case 0xBD5A88AAUL:
			return ((end_pos) >= (read_pos + 13UL));
			break;
		case NET_ONFOLLOW:
			return ((end_pos) >= (read_pos + 15UL));
			break;
		case NET_ONQUEST_0:
			return ((end_pos) >= (read_pos + 8UL));
			break;
		case 0xBD5AA7AAUL:
			return ((end_pos) >= (read_pos + 4UL));
			break;
		case NET_ONSCORES:
			return ((end_pos) >= (read_pos + 3204UL));
			break;
		case 0xFFFFFFFFUL:
			return true;
			break;
		default: {
			DWORD msg_len;

			if ((read_pos + 8uL) > end_pos) {
				return (false);
			}
			EncryptKey(4);
			msg_len = (*(DWORD*) (data + read_pos + 4)) ^ EncryptKey(-4);
			switch ((DWORD) msgid) {
			case NET_ONUSERHOLOSTR:
			case NET_ONAUTOMAPSINFO:
			case 0xBD5AA4AAUL:
			case NET_ONQUEST_1:
			case NET_ONMSGDATA:
			case NET_ONCHECKUID3:
			case 0xBD5A7FAAUL:
			case NET_ONGLOBALINFO:
			case NET_ONRUNCLIENTSCRIPT:
			case 0xBD5A70AAUL:
			case NET_ONCOMBATRESULT:
			case NET_ONCRITTERMOVEITEM:
			case NET_ONCRAFTASK:
			case NET_ONCHECKUID2:
			case 0xBD5A3CAAUL:
			case NET_ONMAPTEXT:
			case NET_ONTEXT:
			case 0xBD5A1FAAUL:
			case NET_ONITEMLEXEMS:
			case 0xBD5A15AA:
			case NET_ONADDCRITTER_2:
			case 0xBD5A0AAAUL:
			case 0xBD5A03AAUL:
			case NET_ONCHECKUID4:
			case NET_ONHOLOINFO:
			case NET_ONPROTOITEMDATA:
			case NET_ONGLOBALENTRANCES:
			case NET_ONMAP:
			case NET_ONCHOSENTALK:
			case NET_ONCONTAINERINFO:
			case NET_ONMAPTEXTMSGLEX:
			case NET_ONTEXTMSG_1:
			case NET_ONCHECKUID1:
			case NET_ONCRITTERLEXEMS:
			case NET_ONADDCRITTER_1:
			case NET_ONCHECKUID0:
				return ((end_pos) >= (read_pos + msg_len));
				break;
			default: {
				LOG_MSG(
						"[BufferManager::NeedProcess] Unknown message<" << ((WORD)(msgid >> 8)) << "> in buffer, try find valid.\n");

				SeekValidMsg();
				if ((read_pos + 4uL) > end_pos) {
					return (false);
				}
			}
				break;
			}
		}
			break;
		}
	}
	return (false);
}

void BufferManager::Refresh(void) {
	if (!error) {
		if (read_pos <= end_pos) {
			if (read_pos) {
				DWORD readpos;
				DWORD endpos;
				BYTE byte;

				readpos = read_pos;
				endpos = end_pos;

				if (readpos < endpos) {
					do {
						byte = *(data + readpos++);
						*(data - read_pos + readpos - 1) = byte;
					}
					while (readpos < end_pos);
				}
				end_pos -= read_pos;
				read_pos = 0;
			}
		}
		else {
			error = true;
			LOG_MSG("[BufferManager::Refresh] Read beyond end position of the buffer!\n");
		}
	}
}

bool BufferManager::SeekValidMsg(void) {
	bool ret_val;

	if ((read_pos + sizeof(DWORD)) > end_pos) {
		ret_val = Reset();
	}
	else {
		for (;;) {
			int key;

			if (is_encrypted) {
				key = keys_table[key_index];
				if ((key_index < 0) || (key_index >= 50)) {
					key_index %= 50;
					if (key_index < 0) {
						key_index += 50;
					}
				}
			}
			else {
				key = 0L;
			}
			// search for valid message
			if (IsValidMsg(key ^ *(DWORD*) (data + read_pos))) {
				ret_val = (true);
				break;
			}
			read_pos++;
			if (is_encrypted) {
				key_index++;
				if ((key_index < 0) || (key_index >= 50)) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			if ((read_pos + sizeof(DWORD)) > end_pos) {
				ret_val = Reset();
				break;
			}
		}
	}

	return (ret_val);
}

void BufferManager::SetEncryptKey(DWORD seed) {
	Randomizer crand;
	DWORD index, indexer;
	DWORD key1, key2;

	if (seed) {
		crand.Generate(seed);
		index = crand.getRandTable()[624];

		indexer = 0;
		do {
			/* generate key #1 */
			if (index >= 624) {
				crand.GenerateState();
				index = crand.getRandTable()[624];
			}
			key1 = FO_CRYPT(crand.getRandTable()[index]);
			key1 ^= (key1 >> 18);
			index++;
			crand.getRandTable()[624] = index;
			/* generate key #2 */
			if (index >= 624) {
				crand.GenerateState();
				index = crand.getRandTable()[624];
			}
			key2 = FO_CRYPT(crand.getRandTable()[index]);
			key2 ^= (key2 >> 18);
			index++;
			crand.getRandTable()[624] = index;

			indexer++;
			keys_table[indexer - 1] = ((DWORDLONG) (LONGLONG) (61440.0 * 2.328306436538696e-10 * (double) key1) + 4096)
					| ((DWORDLONG) (LONGLONG) ((double) key2 * 2.328306436538696e-10 * 61440.0) + 4096);
		}
		while (indexer < 50);

		key_index = 0L;
		is_encrypted = true;
	}
	else {
		is_encrypted = false;
	}
}

DWORD BufferManager::SetEndPos(DWORD endpos) {
	end_pos = endpos;
	return (endpos);
}

/// @warning Function shall not be used! */
DWORD BufferManager::SkipMsg(DWORD msgid) {
	return (0uL);
}

BufferManager &BufferManager::operator<<(bool i) {
	if (!error) {
		char key;

		if ((end_pos + sizeof(bool)) >= len) {
			GrowBuf(sizeof(bool));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(bool);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0;
		}
		*(char*) (data + end_pos) = (i != 0) ^ (key);
		end_pos += sizeof(char);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(bool &i) {
	if (!error) {
		if ((read_pos + sizeof(bool)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(bool)] Read beyond end position of the buffer!\n");
		}
		else {
			char key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(bool);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0;
			}
			i = (key) ^ *(char*) (data + read_pos);
			read_pos += sizeof(char);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(char i) {
	if (!error) {
		char key;

		if ((end_pos + sizeof(char)) >= len) {
			GrowBuf(sizeof(char));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(char);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0;
		}
		*(char*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(char);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(char &i) {
	if (!error) {
		if ((read_pos + sizeof(char)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(char)] Read beyond end position of the buffer!\n");
		}
		else {
			char key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(char);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0;
			}
			i = (key) ^ *(char*) (data + read_pos);
			read_pos += sizeof(char);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(short i) {
	if (!error) {
		short key;

		if ((end_pos + sizeof(short)) >= len) {
			GrowBuf(sizeof(short));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(short);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0;
		}
		*(short*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(short);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(short &i) {
	if (!error) {
		if ((read_pos + sizeof(short)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(short)] Read beyond end position of the buffer!\n");
		}
		else {
			short key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(short);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0;
			}
			i = (key) ^ *(short*) (data + read_pos);
			read_pos += sizeof(short);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(int i) {
	if (!error) {
		int key;

		if ((end_pos + sizeof(int)) >= len) {
			GrowBuf(sizeof(int));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(int);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0L;
		}
		*(int*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(int);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(int &i) {
	if (!error) {
		if ((read_pos + sizeof(int)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(int)] Read beyond end position of the buffer!\n");
		}
		else {
			int key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(int);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0L;
			}
			i = (key) ^ *(int*) (data + read_pos);
			read_pos += sizeof(int);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(unsigned char i) {
	if (!error) {
		unsigned char key;

		if ((end_pos + sizeof(unsigned char)) >= len) {
			GrowBuf(sizeof(unsigned char));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(unsigned char);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0;
		}
		*(unsigned char*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(unsigned char);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(unsigned char &i) {
	if (!error) {
		if ((read_pos + sizeof(unsigned char)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(unsigned char)] Read beyond end position of the buffer!\n");
		}
		else {
			unsigned char key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(unsigned char);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0;
			}
			i = (key) ^ *(unsigned char*) (data + read_pos);
			read_pos += sizeof(unsigned char);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(unsigned short i) {
	if (!error) {
		unsigned short key;

		if ((end_pos + sizeof(unsigned short)) >= len) {
			GrowBuf(sizeof(unsigned short));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(unsigned short);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0;
		}
		*(unsigned short*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(unsigned short);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(unsigned short &i) {
	if (!error) {
		if ((read_pos + sizeof(unsigned short)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(unsigned short)] Read beyond end position of the buffer!\n");
		}
		else {
			unsigned short key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(unsigned short);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0;
			}
			i = (key) ^ *(unsigned short*) (data + read_pos);
			read_pos += sizeof(unsigned short);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(unsigned int i) {
	if (!error) {
		unsigned int key;

		if ((end_pos + sizeof(unsigned int)) >= len) {
			GrowBuf(sizeof(unsigned int));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(unsigned int);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0L;
		}
		*(unsigned int*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(unsigned int);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(unsigned int &i) {
	if (!error) {
		if ((read_pos + sizeof(unsigned int)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(int)] Read beyond end position of the buffer!\n");
		}
		else {
			unsigned int key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(unsigned int);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0L;
			}
			i = (key) ^ *(unsigned int*) (data + read_pos);
			read_pos += sizeof(unsigned int);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator<<(DWORD i) {
	if (!error) {
		DWORD key;

		if ((end_pos + sizeof(DWORD)) >= len) {
			GrowBuf(sizeof(DWORD));
		}
		if (is_encrypted) {
			key = keys_table[key_index];
			key_index += sizeof(DWORD);
			if (key_index < 0 || key_index >= 50) {
				key_index %= 50;
				if (key_index < 0)
					key_index += 50;
			}
		}
		else {
			key = 0L;
		}
		*(DWORD*) (data + end_pos) = (i) ^ (key);
		end_pos += sizeof(DWORD);
	}
	return (*this);
}

BufferManager &BufferManager::operator>>(DWORD &i) {
	if (!error) {
		if ((read_pos + sizeof(DWORD)) > (end_pos)) {
			error = true;
			LOG_MSG("[BufferManager::operator >>(DWORD)] Read beyond end position of the buffer!\n");
		}
		else {
			DWORD key;

			if (is_encrypted) {
				key = keys_table[key_index];
				key_index += sizeof(DWORD);
				if (key_index < 0 || key_index >= 50) {
					key_index %= 50;
					if (key_index < 0)
						key_index += 50;
				}
			}
			else {
				key = 0L;
			}
			i = (key) ^ *(DWORD*) (data + read_pos);
			read_pos += sizeof(DWORD);
		}
	}
	return (*this);
}

BufferManager &BufferManager::operator=(BufferManager const &i) {
	error = i.error;
	len = i.len;
	end_pos = i.end_pos;
	read_pos = i.read_pos;
	SAFEDELA(data);
	data = new char[i.len];
	memcpy(data, i.data, i.len);
	is_encrypted = i.is_encrypted;
	key_index = i.key_index;
	memcpy(keys_table, i.keys_table, sizeof(i.keys_table));
	return (*this);
}
